/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bytedance.scenedemo.animatiom.sipnner;

import com.bytedance.scenedemo.ResourceTable;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.element.Element;
import ohos.agp.utils.Rect;
import ohos.app.Context;

/**
 * Description: 依附于某个View的弹窗，弹窗会出现在目标的上方或下方，如果你想要出现在目标的左边或者右边，请使用HorizontalAttachPopupView。
 * 支持通过popupPosition()方法手动指定想要出现在目标的上边还是下边，但是对Left和Right则不生效。
 *
 * @since 2021-06-17
 */
public abstract class AttachPopupView extends BasePopupView {
    /**
     * BG_DRAWABLE_MARGIN
     */
    protected static final int BG_DRAWABLE_MARGIN = 6;
    private static final int SECOND = 2;
    private static final int FOUR = 4;
    private static final int TEN = 10;
    private static final float SECOND_F = 2f;
    /**
     * defaultOffsetY
     */
    protected int defaultOffsetY = 0;
    /**
     * defaultOffsetX
     */
    protected int defaultOffsetX = 0;
    /**
     * attachPopupContainer
     */
    protected PartShadowContainer attachPopupContainer;
    /**
     * 执行倚靠逻辑
     */
    private float translationX = 0;
    private float translationY = 0;
    /**
     * 是否显示在上部
     */
    private boolean isShowUp;
    /**
     * 是否显示在左边
     */
    private boolean isShowLeft;
    /**
     * 弹窗显示的位置不能超越Window高度
     */
    private float maxY = PopupUtils.getScreenHeight(getContext());
    private int overflow = PopupUtils.vp2px(getContext(), TEN);
    private float centerY = 0;

    /**
     * 构造方法
     *
     * @param context Context
     * @param popupInfo PopupInfo
     */
    public AttachPopupView(Context context, PopupInfo popupInfo) {
        super(context, popupInfo);
        attachPopupContainer = (PartShadowContainer) findComponentById(ResourceTable.Id_attachPopupContainer);
    }

    /**
     * 添加布局
     */
    protected void addInnerContent() {
        Component contentView = LayoutScatter.getInstance(getContext()).parse(getImplLayoutId(),
                attachPopupContainer, false);
        attachPopupContainer.addComponent(contentView);
    }

    @Override
    protected int getPopupLayoutId() {
        return ResourceTable.Layout_popup_attach_popup_view;
    }

    @Override
    protected void initPopupContent() {
        super.initPopupContent();
        if (attachPopupContainer.getChildCount() == 0) {
            addInnerContent();
        }
        if (getPopupInfo().getAtView() == null && getPopupInfo().getTouchPoint() == null) {
            throw new IllegalArgumentException(
                    "atView() or watchView() " + "must be call for AttachPopupView before show()！");
        }

        defaultOffsetY = getPopupInfo().getOffsetY() == 0 ? PopupUtils.vp2px(getContext(), FOUR)
                : getPopupInfo().getOffsetY();
        defaultOffsetX = getPopupInfo().getOffsetX();

        attachPopupContainer.setTranslationX(getPopupInfo().getOffsetX());
        attachPopupContainer.setTranslationY(getPopupInfo().getOffsetY());
        applyBg();
        PopupUtils.applyPopupSize((ComponentContainer) getPopupContentView(),
                getPopupInfo(), new Runnable() {
                    @Override
                    public void run() {
                        doAttach();
                    }
                });
    }

    /**
     * 设置背景
     */
    protected void applyBg() {
        if (!isCreated) {
            // 优先使用implView的背景
            if (getPopupImplView().getBackgroundElement() == null) {
                defaultOffsetX -= BG_DRAWABLE_MARGIN;
                defaultOffsetY -= BG_DRAWABLE_MARGIN;
                attachPopupContainer.setBackground(PopupUtils.createDrawable(PopupUtils.getColor(getContext(),
                        ResourceTable.Color_popup_light_color), getPopupInfo().getBorderRadius()));
            } else {
                Element backgroundElement = getPopupImplView().getBackgroundElement();
                if (backgroundElement != null) {
                    attachPopupContainer.setBackground(backgroundElement);
                    getPopupImplView().setBackground(null);
                }
            }
        }
    }

    /**
     * doAttach
     */
    public void doAttach() {
        maxY = PopupUtils.getScreenHeight(getContext()) - overflow;
        final boolean isRtl = PopupUtils.isLayoutRtl(getContext());
        if (getPopupInfo().getTouchPoint() != null) {
            if (Popup.getLongClickPoint() != null) {
                getPopupInfo().setTouchPoint(Popup.getLongClickPoint());
            }
            centerY = getPopupInfo().getTouchPoint().getY();
            isShowUp = false; // 依附于指定点,尽量优先放在下方，当不够的时候在显示在上方
            if ((getPopupInfo().getTouchPoint().getY() + getPopupContentView().getHeight())
                    > maxY - getStatusBarHeight()) { // 如果下方放不下，超出window高度
                if (getPopupInfo().getTouchPoint().getY()
                        > (float) PopupUtils.getScreenHeight(getContext()) / SECOND) { // 如果触摸点在屏幕中心的下方
                    isShowUp = true;
                }
            }
            isShowLeft = getPopupInfo().getTouchPoint().getX()
                    < (float) PopupUtils.getWindowWidth(getContext()) / SECOND;

            setCompintentMaxWidthAndHeight();

            doAttach2(isRtl);
        } else {
            // 依附于指定View
            // 1. 获取atView在屏幕上的位置
            int[] locations = new int[SECOND];
            int[] rectOld = getPopupInfo().getAtView().getLocationOnScreen();
            locations[0] = rectOld[0];
            locations[1] = rectOld[1];
            final Rect rect = new Rect(locations[0], locations[1], locations[0] + getPopupInfo().getAtView().getWidth(),
                    locations[1] + getPopupInfo().getAtView().getHeight());
            final int centerX = (rect.left + rect.right) / SECOND;

            // 尽量优先放在下方，当不够的时候在显示在上方
            centerY = (float) (rect.top + rect.bottom) / SECOND;
            isShowUp = false;
            if ((rect.bottom + getPopupContentView().getHeight()) > maxY + getStatusBarHeight()) { // 如果下方放不下，超出window高度
                if (centerY > (float) PopupUtils.getScreenHeight(getContext()) / SECOND) { // 如果触摸点在屏幕中心的下方
                    isShowUp = true;
                }
            }
            isShowLeft = centerX < PopupUtils.getWindowWidth(getContext()) / SECOND;

            // 修正高度，弹窗的高有可能超出window区域
            if (!isCreated) {
                setComponentConfig(rect);
            }
            doAttach3(isRtl, rect);
        }
        translationY -= getStatusBarHeight(); // 减去状态栏的高度
        getPopupContentView().setTranslationX(translationX);
        getPopupContentView().setTranslationY(translationY);
        initAndStartAnimation();
    }

    private void setCompintentMaxWidthAndHeight() {
        // 限制最大宽高
        ComponentContainer.LayoutConfig params = getPopupContentView().getLayoutConfig();
        int maxHeight = (int) (isShowUpToTarget() ? (getPopupInfo().getTouchPoint().getY()
                - PopupUtils.getStatusBarHeight(this) - overflow)
                : (PopupUtils.getScreenHeight(getContext()) - getPopupInfo().getTouchPoint().getY() - overflow));
        int maxWidth = (int) (isShowLeft ? (PopupUtils.getWindowWidth(getContext())
                - getPopupInfo().getTouchPoint().getX() - overflow)
                : (getPopupInfo().getTouchPoint().getX() - overflow));
        if (getPopupContentView().getHeight() > maxHeight) {
            params.height = maxHeight;
        }
        if (getPopupContentView().getHeight() > maxWidth) {
            params.width = maxWidth;
        }
        getPopupContentView().setLayoutConfig(params);
    }

    private void setComponentConfig(Rect rect) {
        ComponentContainer.LayoutConfig params = getPopupContentView().getLayoutConfig();
        int maxHeight = isShowUpToTarget() ? (rect.top - PopupUtils.getStatusBarHeight(this) - overflow)
                : (PopupUtils.getScreenHeight(getContext()) - rect.bottom - overflow);
        int maxWidth = isShowLeft ? (PopupUtils.getWindowWidth(getContext())
                - rect.left - overflow) : (rect.right - overflow);
        if (getPopupContentView().getHeight() > maxHeight) {
            params.height = maxHeight;
        }
        if (getPopupContentView().getWidth() > maxWidth) {
            params.width = maxWidth;
        }
        getPopupContentView().setLayoutConfig(params);
    }

    private void doAttach3(boolean isRtl, Rect rect) {
        if (isRtl) {
            translationX = isShowLeft ? -(PopupUtils.getWindowWidth(getContext())
                    - rect.left - getPopupContentView().getWidth() - defaultOffsetX)
                    : -(PopupUtils.getWindowWidth(getContext()) - rect.right + defaultOffsetX);
        } else {
            translationX = isShowLeft ? (rect.left + defaultOffsetX)
                    : (rect.right - getPopupContentView().getWidth() - defaultOffsetX);
        }
        if (getPopupInfo().isCenterHorizontal()) {
            // 水平居中
            if (isShowLeft) {
                if (isRtl) {
                    translationX -= (rect.getWidth() - getPopupContentView().getWidth()) / SECOND_F;
                } else {
                    translationX += (rect.getWidth() - getPopupContentView().getWidth()) / SECOND_F;
                }
            } else {
                if (isRtl) {
                    translationX += (rect.getWidth() - getPopupContentView().getWidth()) / SECOND_F;
                } else {
                    translationX -= (rect.getWidth() - getPopupContentView().getWidth()) / SECOND_F;
                }
            }
        }
        if (isShowUpToTarget()) {
            // 说明上面的空间比较大，应显示在atView上方
            // translationX: 在左边就和atView左边对齐，在右边就和其右边对齐
            translationY = rect.top - getPopupContentView().getHeight() - defaultOffsetY;
        } else {
            translationY = rect.bottom + defaultOffsetY;
        }
    }

    private void doAttach2(boolean isRtl) {
        if (isRtl) {
            translationX = isShowLeft ? -(PopupUtils.getWindowWidth(getContext())
                    - getPopupInfo().getTouchPoint().getX() - getPopupContentView().getWidth() - defaultOffsetX)
                    : -(PopupUtils.getWindowWidth(getContext()) - getPopupInfo().getTouchPoint().getX()
                    + defaultOffsetX);
        } else {
            translationX = isShowLeft ? (getPopupInfo().getTouchPoint().getX() + defaultOffsetX)
                    : (getPopupInfo().getTouchPoint().getX() - getPopupContentView().getWidth() - defaultOffsetX);
        }
        if (getPopupInfo().isCenterHorizontal()) {
            // 水平居中
            if (isShowLeft) {
                if (isRtl) {
                    translationX += getPopupContentView().getHeight() / SECOND_F;
                } else {
                    translationX -= getPopupContentView().getHeight() / SECOND_F;
                }
            } else {
                if (isRtl) {
                    translationX -= getPopupContentView().getWidth() / SECOND_F;
                } else {
                    translationX += getPopupContentView().getWidth() / SECOND_F;
                }
            }
        }
        if (isShowUpToTarget()) {
            // 应显示在point上方
            // translationX: 在左边就和atView左边对齐，在右边就和其右边对齐
            translationY = getPopupInfo().getTouchPoint().getY() - getPopupContentView().getHeight() - defaultOffsetY;
        } else {
            translationY = getPopupInfo().getTouchPoint().getY() + defaultOffsetY;
        }
    }

    /**
     * 初始化和开始动画
     */
    protected void initAndStartAnimation() {
        initAnimator();
        doShowAnimation();
        doAfterShow();
    }

    /**
     * 设置状态栏的高度，用以修正自定义位置弹窗的高度
     *
     * @return int
     */
    protected abstract int setStatusBarHeight();

    /**
     * getStatusBarHeight
     *
     * @return 状态栏高度
     */
    public int getStatusBarHeight() {
        return setStatusBarHeight();
    }

    /**
     * 是否显示在目标上方
     *
     * @return boolean
     */
    protected boolean isShowUpToTarget() {
        if (getPopupInfo().getDismissOnTouchOutside()) {
            // 目标在屏幕上半方，弹窗显示在下；反之，则在上
            return centerY > (float) PopupUtils.getScreenHeight(getContext()) / SECOND;
        }
        return (isShowUp || getPopupInfo().getPopupPosition() == PopupPosition.Top)
                && getPopupInfo().getPopupPosition() != PopupPosition.Bottom;
    }

    @Override
    protected PopupAnimator getPopupAnimator() {
        // 在下方展示
        return new ScrollScaleAnimator(getPopupContentView(), isShowLeft ? PopupAnimation.ScrollAlphaFromLeftTop
                : PopupAnimation.ScrollAlphaFromRightTop);
    }
}
